;;  Machine Description for TI MSP43* processors
;;  Copyright (C) 2013-2023 Free Software Foundation, Inc.
;;  Contributed by Red Hat.

;; This file is part of GCC.

;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.

;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3.  If not see
;; <http://www.gnu.org/licenses/>.


(define_constants
  [
   (PC_REGNO 0)
   (SP_REGNO 1)
   (CARRY 2)
  ])

(define_c_enum "unspec"
  [
   UNS_PROLOGUE_START_MARKER
   UNS_PROLOGUE_END_MARKER
   UNS_EPILOGUE_START_MARKER
   UNS_EPILOGUE_HELPER

   UNS_PUSHM
   UNS_POPM

   UNS_GROW_AND_SWAP
   UNS_SWAP_AND_SHRINK
   
   UNS_DINT
   UNS_EINT
   UNS_PUSH_INTR
   UNS_POP_INTR
   UNS_BIC_SR
   UNS_BIS_SR

   UNS_REFSYM_NEED_EXIT

   UNS_DELAY_32
   UNS_DELAY_32X
   UNS_DELAY_16
   UNS_DELAY_16X
   UNS_DELAY_2
   UNS_DELAY_1
   UNS_DELAY_START
   UNS_DELAY_END
  ])

;; Instruction length is calculated by examining the type and number of
;; operands.
;; Whether the insn uses the 430X extension word, or is a 430X address
;; instruction also has an effect.
;; "Cheap" source operands do not contribute to the overall length of the insn
;; and are register (Rn), indirect post-increment (@Rn+) and indirect register
;; (@Rn).
;; The lengths of instructions in bytes are:
;; Single-op 430: Cheap op == 2
;; (also CALLA)   Other op == 4
;; Double-op 430: Source is not cheap == 2
;;  (also MOVA,   Dest is register == 2
;;   CMPA, ADDA,  Dest is not a register == 4
;;   SUBA)	  (sum the source and dest cost)
;; Single-op 430X: For insn names ending in 'X' add 2 to single-op 430 cost.
;; Double-op 430X: Insn name ends in 'M' == 2
;;		   Others have the same cost as double-op 430 but add 2.
;;
;; The insn type describes whether it is a single or double operand MSP430
;; instruction (some single-operand GCC instructions are actually
;; double-operand on the target).
;; "triple" and "cmp" types use the costs of a double operand type but
;; instead assume that the src operand is in op2, and also cmp types assume the
;; dst operand is in op1.
;; This attribute also describes which operands are safe to examine
;; when calculating the length or extension.  GCC will segfault trying to
;; examine a non-existant operand of an insn.
(define_attr "type" "none,single,double,triple,cmp" (const_string "none"))

;; The M extension is for instructions like RRAM - they always
;; only, and the operand must be a register.
(define_attr "extension" "none,x,a,m"
 (cond [(eq_attr "type" "none")
	(const_string "none")
	(match_operand 0 "msp430_high_memory_operand" "")
	(const_string "x")
	(and (eq_attr "type" "double")
	     (match_operand 1 "msp430_high_memory_operand" ""))
	(const_string "x")
	(and (ior (eq_attr "type" "triple") (eq_attr "type" "cmp"))
	     (ior (match_operand 1 "msp430_high_memory_operand" "")
		  (match_operand 2 "msp430_high_memory_operand" "")))
	(const_string "x")]
	(const_string "none")))

;; Multiply the default length by this constant value.
(define_attr "length_multiplier" "" (const_int 1))

;; Add an additional amount to the total length of the insn.
(define_attr "extra_length" "" (const_int 0))

;; FIXME for some reason if we move the addition of 2 for extension == x to
;; ADJUST_INSN_LENGTH, codesize gets much worse.
(define_attr "length" ""
 (cond [(eq_attr "extension" "m")
	(const_int 2)
	(eq_attr "type" "single")
	(plus (if_then_else (match_operand 0 "msp430_cheap_operand" "")
			    (const_int 2)
			    (const_int 4))
	      (if_then_else (eq_attr "extension" "x")
			    (const_int 2)
			    (const_int 0)))
	(eq_attr "type" "double")
	(plus (plus (if_then_else (match_operand 0 "register_operand" "")
				   (const_int 2)
				   (const_int 4))
		    (if_then_else (match_operand 1 "msp430_cheap_operand" "")
				  (const_int 0)
				  (const_int 2)))
	      (if_then_else (eq_attr "extension" "x")
			    (const_int 2)
			    (const_int 0)))
	(eq_attr "type" "triple")
	(plus (plus (if_then_else (match_operand 0 "register_operand" "")
				   (const_int 2)
				   (const_int 4))
		    (if_then_else (match_operand 2 "msp430_cheap_operand" "")
				  (const_int 0)
				  (const_int 2)))
	      (if_then_else (eq_attr "extension" "x")
			    (const_int 2)
			    (const_int 0)))
	(eq_attr "type" "cmp")
	(plus (plus (if_then_else (match_operand 1 "register_operand" "")
				   (const_int 2)
				   (const_int 4))
		    (if_then_else (match_operand 2 "msp430_cheap_operand" "")
				  (const_int 0)
				  (const_int 2)))
	      (if_then_else (eq_attr "extension" "x")
			    (const_int 2)
			    (const_int 0)))]
  (const_int 2)))

(include "predicates.md")
(include "constraints.md")

(define_mode_iterator QHI [QI HI PSI])
(define_mode_iterator HPSI [HI PSI])
(define_mode_iterator HDI [HI PSI SI DI])

;; Mapping of all shift operators
(define_code_iterator any_shift [ashift ashiftrt lshiftrt])

;; Base name for define_insn
(define_code_attr shift_insn
  [(ashift "ashl") (lshiftrt "lshr") (ashiftrt "ashr")])

;; There are two basic "family" tests we do here:
;;
;; msp430x - true if 430X instructions are available.
;; TARGET_LARGE - true if pointers are 20-bits
;;
;; Note that there are three supported cases, since the base 430
;; doesn't have 20-bit pointers:
;;
;; 1. MSP430 cpu, small model
;; 2. MSP430X cpu, small model.
;; 3. MSP430X cpu, large model.

;;------------------------------------------------------------
;; Moves

;; Push/Pop must be before the generic move patterns

(define_insn "push"
  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNO)))
	(match_operand:HI 0 "register_operand" "r"))]
  ""
  "PUSH\t%0"
  [(set_attr "type" "single")]
)

(define_insn "pusha"
  [(set (mem:PSI (pre_dec:PSI (reg:PSI SP_REGNO)))
	(match_operand:PSI 0 "register_operand" "r"))]
  "TARGET_LARGE"
  "PUSHX.A\t%0"
  [(set_attr "type" "single")
   (set_attr "extension" "x")]
)

(define_insn "pushm"
  [(unspec_volatile [(match_operand 0 "register_operand" "r")
		     (match_operand 1 "immediate_operand" "n")] UNS_PUSHM)]
  ""
  "PUSHM%b0\t%1, %0"
  [(set_attr "type" "single")
   (set_attr "extension" "m")]
)

(define_insn "pop"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mem:HI (post_inc:HI (reg:HI SP_REGNO))))]
  ""
  "POP\t%0"
  [(set_attr "type" "single")]
)

(define_insn "popa"
  [(set (match_operand:PSI 0 "register_operand" "=r")
	(mem:PSI (post_inc:PSI (reg:PSI SP_REGNO))))]
  "TARGET_LARGE"
  "POPX.A\t%0"
  [(set_attr "type" "single")
   (set_attr "extension" "x")]
)

;; This is nasty.  Operand0 is bogus.  It is only there so that we can get a
;; mode for the %b0 to work.  We should use operand1 for this, but that does
;; not have a mode.
;; 
;; Operand1 is actually a register, but we cannot accept (REG...) because the
;; cprop_hardreg pass can and will renumber registers even inside
;; unspec_volatiles.  So we take an integer register number parameter and
;; fudge it to be a register name when we generate the assembler.
;;
;; The pushm pattern does not have this problem because of all of the
;; frame info cruft attached to it, so cprop_hardreg leaves it alone.
(define_insn "popm"
  [(unspec_volatile [(match_operand 0 "register_operand" "r")
		     (match_operand 1 "immediate_operand" "i")
		     (match_operand 2 "immediate_operand" "i")] UNS_POPM)]
  ""
  "POPM%b0\t%2, r%J1"
  [(set_attr "type" "single")
   (set_attr "extension" "m")]
)

;; The next two patterns are here to support a "feature" of how GCC implements
;; varargs.  When a function uses varargs and the *second* to last named
;; argument is split between argument registers and the stack, gcc expects the
;; callee to allocate space on the stack that can contain the register-based
;; part of the argument.  This space *has* to be just before the remaining
;; arguments (ie the ones that are fully on the stack).
;;
;; The problem is that the MSP430 CALL instruction pushes the return address
;; onto the stack in the exact place where the callee wants to allocate
;; this extra space.  So we need a sequence of instructions that can allocate
;; the extra space and then move the return address down the stack, so that
;; the extra space is now adjacent to the remaining arguments.
;;
;; This could be constructed through regular insns, but they might be split up
;; by a misguided optimization, so an unspec volatile is used instead.

(define_insn "grow_and_swap"
  [(unspec_volatile [(const_int 0)] UNS_GROW_AND_SWAP)]
  ""
  "*
    if (TARGET_LARGE)
      return \"SUBA\t#2, r1 { MOVX.A\t2(r1), 0(r1)\";
    return \"SUB\t#2, r1 { MOV.W\t2(r1), 0(r1)\";
  "
  [(set (attr "length")
	(if_then_else (match_test "TARGET_LARGE")
		      (const_int 8)
		      (const_int 6)))]
)

(define_insn "swap_and_shrink"
  [(unspec_volatile [(const_int 0)] UNS_SWAP_AND_SHRINK)]
  ""
  "* return TARGET_LARGE
	   ? \"MOVX.A\t0(r1), 2(r1) { ADDA\t#2, SP\"
	   : \"MOV.W\t0(r1), 2(r1) { ADD\t#2, SP\";
  "
  [(set (attr "length")
	(if_then_else (match_test "TARGET_LARGE")
		      (const_int 10)
		      (const_int 8)))]
)

; I set LOAD_EXTEND_OP and WORD_REGISTER_OPERATIONS, but gcc puts in a
; zero_extend anyway.  Catch it here.
(define_insn "movqihi"
  [(set (match_operand:HI                 0 "register_operand" "=r,r")
	(zero_extend:HI (match_operand:QI 1 "memory_operand" "Ys,m")))]
  ""
  "@
   MOV.B\t%1, %0
   MOV%X1.B\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn "movqi_topbyte"
  [(set (match_operand:QI 0 "msp430_general_dst_operand" "=r")
	(subreg:QI (match_operand:PSI 1 "msp430_general_operand" "r") 2))]
  "msp430x"
  "PUSHM.A\t#1,%1 { POPM.W\t#1,%0 { POPM.W\t#1,%0"
  [(set_attr "length" "6")
   (set_attr "type" "double")]
)

(define_insn "movqi"
  [(set (match_operand:QI 0 "msp430_general_dst_operand" "=rYsYx,rm")
	(match_operand:QI 1 "msp430_general_operand" "riYsYx,rmi"))]
  ""
  "@
  MOV.B\t%1, %0
  MOVX.B\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn "movhi"
  [(set (match_operand:HI 0 "msp430_general_dst_operand" "=r,rYsYx,rm")
	(match_operand:HI 1 "msp430_general_operand" "N,riYsYx,rmi"))]
  ""
  "@
  MOV.B\t%1, %0
  MOV.W\t%1, %0
  MOVX.W\t%1, %0"
  [(set_attr "type" "double")]
)

(define_expand "movsi"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand")
	(match_operand:SI 1 "general_operand"))]
  ""
  ""
)

(define_insn_and_split "movsi_s"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
	(subreg:SI (match_operand:PSI 1 "msp430_symbol_operand" "i") 0))]
  ""
  ""
  "reload_completed"
  [(set (match_operand:HI 2 "msp430_general_dst_nonv_operand")
	(match_operand:HI 4 "general_operand"))
   (set (match_operand:HI 3 "msp430_general_dst_nonv_operand")
	(match_operand:HI 5 "general_operand"))]
  "msp430_split_movsi (operands);"
  [(set_attr "type" "double")]
)

(define_insn_and_split "movsi_x"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm")
	(match_operand:SI 1 "general_operand" "rmi"))]
  ""
  "#"
  "reload_completed"
  [(set (match_operand:HI 2 "msp430_general_dst_nonv_operand")
	(match_operand:HI 4 "general_operand"))
   (set (match_operand:HI 3 "msp430_general_dst_nonv_operand")
	(match_operand:HI 5 "general_operand"))]
  "msp430_split_movsi (operands);"
  [(set_attr "type" "double")]
)

;; FIXME: Some MOVX.A cases can be done with MOVA, this is only a few of them.
(define_insn "movpsi"
  [(set (match_operand:PSI 0 "msp430_general_dst_operand" "=r,r,r,Ya,rm")
	(match_operand:PSI 1 "msp430_general_operand" "N,O,riYa,r,rmi"))]
  ""
  "@
  MOV.B\t%1, %0
  MOV.W\t%1, %0
  MOVA\t%1, %0
  MOVA\t%1, %0
  MOVX.A\t%1, %0"
  [(set_attr "extension" "none,none,a,a,x")
   (set_attr "type" "double")]
)

; This pattern is identical to the truncsipsi2 pattern except
; that it uses a SUBREG instead of a TRUNC.  It is needed in
; order to prevent reload from converting (set:SI (SUBREG:PSI (SI)))
; into (SET:PSI (PSI)).
;
; Note: using POPM.A #1 is two bytes smaller than using POPX.A....

(define_insn "movsipsi2"
  [(set (match_operand:PSI            0 "register_operand" "=r")
	(subreg:PSI (match_operand:SI 1 "register_operand" "r") 0))]
  "msp430x"
  "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A #1, %0 ; Move reg-pair %L1:%H1 into pointer %0"
  [(set_attr "length" "6")
   (set_attr "type" "double")]
)

;; Produced when converting a pointer to an integer via a union, eg gcc.dg/pr47201.c.
(define_insn "*movpsihi2_lo"
  [(set (match_operand:HI             0 "register_operand" "=r")
	(subreg:HI (match_operand:PSI 1 "msp430_symbol_operand" "i") 0))]
  "msp430x"
  "MOVA\t%1, %0"
  [(set_attr "extension" "a")
   (set_attr "type" "double")]
)

;;------------------------------------------------------------
;; Math

(define_insn "addpsi3"
  [(set (match_operand:PSI	     0 "msp430_general_dst_operand" "=r,rm")
	(plus:PSI (match_operand:PSI 1 "msp430_general_operand" "%0,0")
		  (match_operand:PSI 2 "msp430_general_operand"      "rLs,rmi")))]
  ""
  "@
  ADDA\t%2, %0
  ADDX.A\t%2, %0"
  [(set_attr "extension" "a,x")
   (set_attr "type" "triple")]
)

(define_insn "addqi3"
  [(set (match_operand:QI	   0 "msp430_general_dst_operand" "=rYsYx,rm")
	(plus:QI (match_operand:QI 1 "msp430_general_operand" "%0,0")
		 (match_operand:QI 2 "msp430_general_operand"      "riYsYx,rmi")))]
  ""
  "@
   ADD.B\t%2, %0
   ADDX.B\t%2, %0"
  [(set_attr "type" "triple")]
)

(define_insn "addhi3"
  [(set (match_operand:HI	    0 "msp430_general_dst_operand" "=rYsYx,rm")
	(plus:HI (match_operand:HI  1 "msp430_general_operand" "%0,0")
		  (match_operand:HI 2 "msp430_general_operand"      "riYsYx,rmi")))]
  ""
  "@
   ADD.W\t%2, %0
   ADDX.W\t%2, %0"
  [(set_attr "type" "triple")]
)

; This pattern is needed in order to avoid reload problems.
; It takes an SI pair of registers, adds a value to them, and
; then converts them into a single PSI register.

(define_insn "addsipsi3"
  [(set (subreg:SI (match_operand:PSI 0 "register_operand" "=&r") 0)
	(plus:SI (match_operand:SI    1 "register_operand" "0")
		 (match_operand       2 "general_operand" "rmi")))]
  ""
  "ADD%X2.W\t%L2, %L0 { ADDC%X2.W\t%H2, %H0 { PUSH.W\t%H0 { PUSH.W\t%L0 { POPM.A\t#1, %0"
  [(set (attr "length")
	(if_then_else (match_operand 2 "register_operand" "")
		      (const_int 10)
		      (if_then_else (match_operand 2 "msp430_high_memory_operand" "")
				    (const_int 18)
				    (const_int 14))))
   (set_attr "type" "triple")]
)

(define_insn "addsi3"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=&rYsYx,rm")
	(plus:SI (match_operand:SI 1 "general_operand" "%0,0")
		 (match_operand:SI 2 "general_operand" "rYsYxi,mi")))]
  ""
  "@
   ADD\t%L2, %L0 { ADDC\t%H2, %H0
   ADDX\t%L2, %L0 { ADDCX\t%H2, %H0"
  [(set_attr "length_multiplier" "2")
   (set_attr "type" "triple")]
)

; Version of addhi that exposes the carry operations, for SImode adds.
;
; NOTE - we are playing a dangerous game with GCC here.  We have these two
; add patterns and the splitter that follows because our tests have shown
; that this results in a significant reduction in code size - because GCC is
; able to discard any unused part of the addition.  We have to annotate the
; patterns with the set and use of the carry flag because otherwise GCC will
; discard parts of the addition when they are actually needed.  But we have
; not annotated all the other patterns that set the CARRY flag as doing so
; results in an overall increase in code size[1].  Instead we just *hope*
; that GCC will not move a carry-setting instruction in between the first
; and second adds.
;
; So far our experiments have shown that GCC is likely to move MOV and CMP
; instructions in between the two adds, but not other instructions.  MOV is
; safe, CMP is not.  So we have annotated the CMP patterns and left the
; subtract, shift and other add patterns alone.  At the moment this is
; working, but with future changes to the generic parts of GCC that might
; change.
;
; [1] It is not clear exactly why the code size increases.  The cause appears
; to be that reload is more prevelent to spilling a variable onto the stack
; but why it does this is unknown.  Possibly the additional CLOBBERs necessary
; to correctly annotate the other patterns makes reload think that there is
; increased register pressure.  Or possibly reload does not handle ADD patterns
; that are not single_set() very well.

; match_operand 3 is likely to be the same as op2 most of the time - except
; when op2 is a post_inc and we have stripped the post_inc from match_operand 3

(define_insn "addhi3_cy"
  [(set (match_operand:HI	   0 "msp430_general_dst_operand" "=rYsYx,rm")
	(plus:HI (match_operand:HI 1 "msp430_general_operand" "%0,0")
		 (match_operand:HI 2 "msp430_nonimmediate_operand" "rYsYxi,rm")))
   (set (reg:BI CARRY)
	(truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1))
					   (zero_extend:SI (match_operand:HI 3 "msp430_nonimmediate_operand" "rYsYxi,rm")))
				  (const_int 16))))
   ]
  ""
  "@
   ADD\t%2, %1 ; cy
   ADDX\t%2, %1 ; cy"
  [(set_attr "type" "triple")]
)

(define_insn "addhi3_cy_i"
  [(set (match_operand:HI	   0 "msp430_general_dst_nonv_operand" "=r,rm")
	(plus:HI (match_operand:HI 1 "general_operand" "%0,0")
		 (match_operand:HI 2 "immediate_operand"     "i,i")))
   (set (reg:BI CARRY)
	(truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 1))
					   (match_operand 3 "immediate_operand" "i,i"))
				  (const_int 16))))
   ]
  ""
  "@
   ADD\t%2, %1 ; cy
   ADD%X0\t%2, %1 ; cy"
  [(set_attr "type" "triple")]
)

; Version of addhi that adds the carry, for SImode adds.
(define_insn "addchi4_cy"
  [(set (match_operand:HI		    0 "msp430_general_dst_operand" "=rYsYx,rm")
	(plus:HI (plus:HI (match_operand:HI 1 "msp430_general_operand" "%0,0")
			  (match_operand:HI 2 "msp430_general_operand"      "riYsYx,rmi"))
		 (zero_extend:HI (reg:BI CARRY))))
   ]
  ""
  "@
   ADDC\t%2, %1
   ADDCX\t%2, %1"
  [(set_attr "type" "triple")]
)

; Split an SImode add into two HImode adds, keeping track of the carry
; so that gcc knows when it can and can't optimize away the two
; halves.
; We use the ugly predicate "msp430_nonsubregnonpostinc_or_imm_operand" to
; enforce the position of a post_inc into op2 if present
(define_split
  [(set (match_operand:SI	   0 "msp430_nonsubreg_dst_operand")
	(plus:SI (match_operand:SI 1 "msp430_nonsubregnonpostinc_or_imm_operand")
		 (match_operand:SI 2 "msp430_nonsubreg_or_imm_operand")))
   ]
  ""
  [(parallel [(set (match_operand:HI 3 "msp430_general_dst_nonv_operand" "=&rm")
		   (plus:HI (match_dup 4)
			    (match_dup 5)))
	      (set (reg:BI CARRY)
		   (truncate:BI (lshiftrt:SI (plus:SI (zero_extend:SI (match_dup 4))
						      (match_dup 9))
					     (const_int 16))))
	      ])
   (set (match_operand:HI 6 "msp430_general_dst_nonv_operand" "=&rm")
	(plus:HI (plus:HI (match_dup 7)
			  (match_dup 8))
		 (zero_extend:HI (reg:BI CARRY))))
   ]
  "
  if (msp430_split_addsi (operands))
    FAIL;
  "
)


;; Alternatives 2 and 3 are to handle cases generated by reload.
(define_insn "subpsi3"
  [(set (match_operand:PSI	      0 "msp430_general_dst_nonv_operand"	"=r,   rm, &?r, ?&r")
	(minus:PSI (match_operand:PSI 1 "general_operand"			"0,   0,   !r,  !i")
		   (match_operand:PSI 2 "general_operand"			"rLs, rmi, rmi,  r")))]
  ""
  "@
  SUBA\t%2, %0
  SUBX.A\t%2, %0
  MOVX.A\t%1, %0 { SUBX.A\t%2, %0
  MOVX.A\t%1, %0 { SUBA\t%2, %0"
  [(set_attr "type" "triple")
   (set_attr "extension" "a,x,x,x")
   (set_attr "length_multiplier" "1,1,2,2")]
)

;; Alternatives 2 and 3 are to handle cases generated by reload.
(define_insn "subqi3"
  [(set (match_operand:QI	    0 "msp430_general_dst_nonv_operand" "=rYsYx,  rm,  &?r, ?&r")
	(minus:QI (match_operand:QI 1 "general_operand"       "0,    0,    !r,  !i")
		  (match_operand:QI 2 "general_operand"      " riYsYx, rmi, rmi,   r")))]
  ""
  "@
  SUB.B\t%2, %0
  SUBX.B\t%2, %0
  MOV%X2.B\t%1, %0 { SUB%X2.B\t%2, %0
  MOV%X0.B\t%1, %0 { SUB%X0.B\t%2, %0"
  [(set_attr "length_multiplier" "1,1,2,2")
   (set_attr "type" "triple")]
)

;; Alternatives 2 and 3 are to handle cases generated by reload.
(define_insn "subhi3"
  [(set (match_operand:HI	    0 "msp430_general_dst_nonv_operand" "=rYsYx,  rm,  &?r, ?&r")
	(minus:HI (match_operand:HI 1 "general_operand"       "0,    0,    !r,  !i")
		  (match_operand:HI 2 "general_operand"      " riYsYx, rmi, rmi,   r")))]
  ""
  "@
  SUB.W\t%2, %0
  SUBX.W\t%2, %0
  MOV%X2.W\t%1, %0 { SUB%X2.W\t%2, %0
  MOV%X0.W\t%1, %0 { SUB%X0.W\t%2, %0"
  [(set_attr "length_multiplier" "1,1,2,2")
   (set_attr "type" "triple")]
)

(define_insn "subsi3"
  [(set (match_operand:SI	    0 "msp430_general_dst_nonv_operand" "=&rYsYx,m")
	(minus:SI (match_operand:SI 1 "general_operand"   "0,0")
		  (match_operand:SI 2 "general_operand"        "riYsYx,mi")))]
  ""
  "@
  SUB\t%L2, %L0 { SUBC\t%H2, %H0
  SUBX\t%L2, %L0 { SUBCX\t%H2, %H0"
  [(set_attr "length_multiplier" "2")
   (set_attr "type" "triple")]
)

(define_insn "*bic<mode>_cg"
  [(set (match_operand:QHI 0 "msp430_general_dst_operand" "=rYs,m")
	(and:QHI (match_operand:QHI 1 "msp430_general_operand" "0,0")
		 (match_operand 2 "msp430_inv_constgen_operator" "n,n")))]
  ""
  "@
   BIC%x0%b0\t#%I2, %0
   BIC%X0%b0\t#%I2, %0"
  [(set_attr "length" "2")	; Smaller length achieved by using constant generator
   (set_attr "type" "double")]
)

(define_insn "bic<mode>3"
  [(set (match_operand:QHI		     0 "msp430_general_dst_operand" "=rYsYx,rm")
	(and:QHI (not:QHI (match_operand:QHI 1 "msp430_general_operand"       "rYsYx,rmn"))
		 (match_operand:QHI	     2 "msp430_general_operand"  "0,0")))]
  ""
  "@
   BIC%x0%b0\t%1, %0
   BICX%b0\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn "and<mode>3"
  [(set (match_operand:QHI 0 "msp430_general_dst_operand" "=r,rYsYx,rm")
	(and:QHI (match_operand:QHI 1 "msp430_general_operand" "%0,0,0")
		 (match_operand:QHI 2 "msp430_general_operand" "N,riYsYx,rmi")))]
  ""
  "@
   AND%x0.B\t%2, %0
   AND%x0%b0\t%2, %0
   ANDX%b0\t%2, %0"
  [(set_attr "type" "triple")]
)

(define_insn "ior<mode>3"
  [(set (match_operand:QHI	    0 "msp430_general_dst_operand" "=rYsYx,rm")
	(ior:QHI (match_operand:QHI 1 "msp430_general_operand" "%0,0")
		 (match_operand:QHI 2 "msp430_general_operand" "riYsYx,rmi")))]
  ""
  "@
   BIS%x0%b0\t%2, %0
   BISX%b0\t%2, %0"
  [(set_attr "type" "triple")]
)

(define_insn "xor<mode>3"
  [(set (match_operand:QHI	    0 "msp430_general_dst_operand" "=rYsYx,rm")
	(xor:QHI (match_operand:QHI 1 "msp430_general_operand" "%0,0")
		 (match_operand:QHI 2 "msp430_general_operand" "riYsYx,rmi")))]
  ""
  "@
   XOR%x0%b0\t%2, %0
   XORX%b0\t%2, %0"
  [(set_attr "type" "triple")]
)

;; Macro : XOR #~0, %0
(define_insn "one_cmpl<mode>2"
  [(set (match_operand:QHI	    0 "msp430_general_dst_operand" "=rYs,m")
	(not:QHI (match_operand:QHI 1 "msp430_general_operand" "0,0")))]
  ""
  "@
   INV%x0%b0\t%0
   INV%X0%b0\t%0"
  [(set_attr "type" "double")]
)

(define_insn "extendqihi2"
  [(set (match_operand:HI		  0 "msp430_general_dst_operand" "=rYs,m")
	(sign_extend:HI (match_operand:QI 1 "msp430_general_operand" "0,0")))]
  ""
  "@
   SXT%X0\t%0
   SXT%X0\t%0"
  [(set_attr "type" "single")]
)

(define_insn "extendqipsi2"
  [(set (match_operand:PSI		   0 "msp430_general_dst_operand" "=r,m")
	(sign_extend:PSI (match_operand:QI 1 "msp430_general_operand" "0,0")))]
  ""
  "@
  SXT\t%0
  SXTX.A\t%0"
  [(set_attr "type" "single")
   (set_attr "extension" "none,x")]
)

;; ------------------------
;; ZERO EXTEND INSTRUCTIONS
;; Byte-writes to registers clear bits 19:8
;;   * Byte-writes to memory do not affect bits 15:8
;; Word-writes to registers clear bits 19:16
;; PSImode writes to memory clear bits 15:4 of the second memory word
;; We define all possible insns since that results in better code than if
;; they are inferred.
;; ------------------------

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI		  0 "msp430_general_dst_operand" "=rYs,r,r,m")
	(zero_extend:HI (match_operand:QI 1 "msp430_general_operand" "0,rYs,m,0")))]
  ""
  "@
   AND\t#0xff, %0
   MOV.B\t%1, %0
   MOV%X1.B\t%1, %0
   AND%X0\t#0xff, %0"
  [(set_attr "type" "double")]
)

(define_insn "zero_extendqipsi2"
  [(set (match_operand:PSI		   0 "register_operand" "=r,r")
	(zero_extend:PSI (match_operand:QI 1 "general_operand" "rYs,m")))]
  "msp430x"
  "@
   MOV.B\t%1, %0
   MOV%X1.B\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=r,r")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,rm")))]
  ""
  "@
  CLR\t%H0
  MOV%X1.B\t%1,%L0 { CLR\t%H0"
  [(set_attr "extra_length" "2")
   (set_attr "length_multiplier" "1,2")
   (set_attr "type" "double")]
)

(define_insn "zero_extendhipsi2"
  [(set (match_operand:PSI		   0 "msp430_general_dst_operand" "=r,r,m")
	(zero_extend:PSI (match_operand:HI 1 "msp430_general_operand"     "rYs,m,r")))]
  "msp430x"
  "@
  MOV.W\t%1, %0
  MOV%X1\t%1, %0
  MOVX.A\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=rm,r")
	(zero_extend:SI (match_operand:HI 1 "general_operand" "0,r")))]
  ""
  "@
  MOV%X0.W\t#0,%H0
  MOV.W\t%1,%L0 { MOV.W\t#0,%H0"
  [(set_attr "length_multiplier" "1,2")
   (set_attr "type" "double")]
)

(define_insn "zero_extendhisipsi2"
  [(set (match_operand:PSI 0 "msp430_general_dst_nonv_operand" "=r,r")
	(subreg:PSI (zero_extend:SI (match_operand:HI 1 "general_operand" "0,r")) 0))]
  "msp430x"
  "@
   AND.W\t#-1,%0
   MOV.W\t%1,%0"
  [(set_attr "length" "4,2")
   (set_attr "type" "double")]
)

; Nasty - we are sign-extending a 20-bit PSI value in one register into
; two adjacent 16-bit registers to make an SI value.  There is no MSP430X
; instruction that will do this, so we push the 20-bit value onto the stack
; and then pop it off as two 16-bit values.
;
; FIXME: The MSP430X documentation does not specify if zero-extension or
; sign-extension happens when the 20-bit value is pushed onto the stack.
; It is probably zero-extension, but if not this pattern will not work
; when the PSI value is negative..
;
; Note: using PUSHM.A #1 is two bytes smaller than using PUSHX.A....
;
; Note: We use a + constraint on operand 0 as otherwise GCC gets confused
; about extending a single PSI mode register into a pair of SImode registers
; with the same starting register.  It thinks that the upper register of
; the pair is unused and so it can clobber it.  Try compiling 20050826-2.c
; at -O2 to see this.

; FIXME we can use MOVA for r->m if m is &abs20 or z16(rdst)
(define_insn "zero_extendpsisi2"
  [(set (match_operand:SI		   0 "register_operand" "+r,m")
	(zero_extend:SI (match_operand:PSI 1 "register_operand" "r,r")))]
  ""
  "@
  * if (REGNO (operands[1]) == SP_REGNO) \
      /* If the source register is the stack pointer, the value \
	 stored in the stack slot will be the value *after* the \
	 stack pointer has been decremented.  So allow for that \
	 here.  */ \
      return \"PUSHM.A\t#1, %1 { ADDX.W\t#4, @r1 { POPX.W\t%L0 { POPX.W\t%H0 ; get stack pointer into %L0:%H0\"; \
    else \
      return \"PUSHM.A\t#1, %1 { POPX.W\t%L0 { POPX.W\t%H0 ; move pointer in %1 into reg-pair %L0:%H0\";
  MOVX.A %1, %0"
  [(set (attr "length")
    (cond [(match_test "REGNO (operands[1]) == SP_REGNO")
	   (const_int 18)
	   (eq_attr "alternative" "1")
	   (const_int 6)]
	   (const_int 10)))
   (set_attr "type" "double")]
)

;; Below are unnamed insn patterns to catch pointer manipulation insns
;; generated by combine.
;; We get large code size bloat when a PSImode pointer is stored in
;; memory, so we try to avoid that where possible and keep point manipulation
;; between registers.
; FIXME many of these should be unnnecessary once combine deals with
; (sign_extend (zero_extend)) or (sign_extend (subreg)) BZ 91865.

;; This is just another way of writing movqipsi/zero_extendqipsi
(define_insn ""
  [(set (match_operand:PSI 0 "register_operand" "=r")
	(sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0)))]
  "msp430x"
  "MOV%X1.B\t%1, %0"
  [(set_attr "type" "double")]
)

(define_insn ""
  [(set (match_operand:PSI				   0 "register_operand" "=r,r")
	(sign_extend:PSI (zero_extend:HI (match_operand:QI 1 "general_operand" "rYs,m"))))]
  "msp430x"
  "@
   MOV.B\t%1, %0
   MOV%X1.B\t%1, %0"
  [(set_attr "type" "double")]
)

;; The next three insns emit identical assembly code.
;; They take a QImode and shift it in SImode.  Only shift counts <= 8
;; are handled since that is the simple case where the high 16-bits (i.e. the
;; high register) are always 0.
(define_insn ""
  [(set (match_operand:SI			     0 "register_operand" "=r,r,r")
	(ashift:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "0,rm,rm"))
		   (match_operand:HI		     2 "const_1_to_8_operand" "M,M,i")))]
  "msp430x"
  "@
  RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
  [(set_attr "length" "4,*,*")
   (set_attr "extra_length" "0,4,6")
   (set_attr "type" "double")]
)

(define_insn ""
  [(set (match_operand:SI			     		0 "register_operand" "=r,r,r")
	(ashift:SI (zero_extend:SI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0))
		   (match_operand:HI		     		2 "const_1_to_8_operand" "M,M,i")))]
  "msp430x"
  "@
  RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
  [(set_attr "length" "4,*,*")
   (set_attr "extra_length" "0,4,6")
   (set_attr "type" "double")]
)

;; Same as above but with a NOP sign_extend round the subreg
(define_insn ""
  [(set (match_operand:SI			     				 0 "register_operand" "=r,r,r")
	(ashift:SI (zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0)))
		   (match_operand:HI		     				 2 "const_1_to_8_operand" "M,M,i")))]
  "msp430x"
  "@
  RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RLAM.W %2, %L0 { CLR %H0
  MOV%X1.B %1, %L0 { RPT %2 { RLAX.W %L0 { CLR %H0"
  [(set_attr "length" "4,*,*")
   (set_attr "extra_length" "0,4,6")
   (set_attr "type" "double")]
)

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "rm") 0))))]
  "msp430x"
  "MOV%X1.B %1, %L0 { CLR %H0"
  [(set_attr "extra_length" "4")
   (set_attr "type" "double")]
)

(define_insn ""
  [(set (match_operand:PSI					  0 "register_operand" "=r,r,r")
	(ashift:PSI (sign_extend:PSI (subreg:HI (match_operand:QI 1 "general_operand" "0,rm,rm") 0))
		    (match_operand:HI				  2 "const_1_to_19_operand" "M,M,i")))]
  "msp430x"
  "@
  RLAM.W %2, %0
  MOV%X1.B %1, %0 { RLAM.W %2, %0
  MOV%X1.B %1, %0 { RPT %2 { RLAX.A %0"
  [(set_attr "length" "2,*,*")
   (set_attr "extra_length" "0,2,4")
   (set_attr "type" "double")]
)
;; END msp430 pointer manipulation combine insn patterns

;; Eliminate extraneous zero-extends mysteriously created by gcc.
(define_peephole2
  [(set (match_operand:HI 0 "register_operand")
	(zero_extend:HI (match_operand:QI 1 "general_operand")))
   (set (match_operand:HI 2 "register_operand")
	(zero_extend:HI (match_operand:QI 3 "register_operand")))]
  "REGNO (operands[0]) == REGNO (operands[2]) && REGNO (operands[2]) == REGNO (operands[3])"
  [(set (match_dup 0)
	(zero_extend:HI (match_dup 1)))]
)

(define_insn "truncpsihi2"
  [(set (match_operand:HI		0 "msp430_general_dst_operand" "=rm")
	(truncate:HI (match_operand:PSI 1 "register_operand"      "r")))]
  ""
  "MOVX\t%1, %0"
  [(set_attr "extension" "m")
   (set_attr "type" "double")]
)

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "msp430_general_dst_nonv_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))]
  ""
  { msp430x_extendhisi (operands, 0); return ""; }
  [(set (attr "length")
	(symbol_ref "msp430x_extendhisi (operands, 1)"))
   (set_attr "type" "double")]
)

(define_insn "extendhipsi2"
  [(set (match_operand:PSI 0 "msp430_general_dst_nonv_operand" "=r")
	(subreg:PSI (sign_extend:SI (match_operand:HI 1 "general_operand" "0")) 0))]
  "msp430x"
  "RLAM.A #4, %0 { RRAM.A #4, %0"
  [(set_attr "length_multiplier" "2")
   (set_attr "extension" "m")
   (set_attr "type" "double")]
)

;; Look for cases where integer/pointer conversions are suboptimal due
;; to missing patterns, despite us not having opcodes for these
;; patterns.  Doing these manually allows for alternate optimization
;; paths.

(define_insn "extend_and_shift1_hipsi2"
  [(set (subreg:SI (match_operand:PSI 0 "msp430_general_dst_nonv_operand" "=r") 0)
	(ashift:SI (sign_extend:SI (match_operand:HI 1 "general_operand" "0"))
		   (const_int 1)))]
  "msp430x"
  "RLAM.A #4, %0 { RRAM.A #3, %0"
  [(set_attr "length_multiplier" "2")
   (set_attr "extension" "m")
   (set_attr "type" "double")]
)

(define_insn "extend_and_shift2_hipsi2"
  [(set (subreg:SI (match_operand:PSI 0 "msp430_general_dst_nonv_operand" "=r") 0)
	(ashift:SI (sign_extend:SI (match_operand:HI 1 "general_operand" "0"))
		   (const_int 2)))]
  "msp430x"
  "RLAM.A #4, %0 { RRAM.A #2, %0"
  [(set_attr "length_multiplier" "2")
   (set_attr "extension" "m")
   (set_attr "type" "double")]
)

;; We also need to be able to sign-extend pointer types (eg ptrdiff_t).
;; Since (we assume) pushing a 20-bit value onto the stack zero-extends
;; it, we use a different method here.

(define_insn "extendpsisi2"
  [(set (match_operand:SI                  0 "register_operand" "=r")
	(sign_extend:SI (match_operand:PSI 1 "register_operand" "r")))]
  "msp430x"
  "*
    /* The intention here is that we copy the bottom 16-bits of
       %1 into %L0 (zeroing the top four bits).  Then we copy the
       entire 20-bits of %1 into %H0 and then arithmetically shift
       it right by 16 bits, to get the top four bits of the pointer
       sign-extended in %H0.  */
    if (REGNO (operands[0]) == REGNO (operands[1]))
      return \"MOVX.A\t%1, %H0 { MOV.W\t%1, %L0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\";
    else
      return \"MOV.W\t%1, %L0 { MOVX.A\t%1, %H0 { RPT\t#16 { RRAX.A\t%H0 ; sign extend pointer in %1 into %L0:%H0\";
  "
  [(set_attr "length" "10")
   (set_attr "type" "double")]
)

; See the movsipsi2 pattern above for another way that GCC performs this
; conversion.
(define_insn "truncsipsi2"
  [(set (match_operand:PSI              0 "register_operand" "=r")
	(truncate:PSI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "PUSH.W\t%H1 { PUSH.W\t%L1 { POPM.A\t#1, %L0"
  [(set_attr "length" "6")
   (set_attr "type" "single")]
)

;;------------------------------------------------------------
;; Shift Functions

;; Note:  We do not use the RPT ... SHIFT instruction sequence
;; when the repeat count is in a register, because even though RPT
;; accepts counts in registers, it does not work if the count is
;; zero, and the actual count in the register has to be one less
;; than the required number of iterations.  We could encode a
;; seqeunce like this:
;;
;;   bit #0xf, Rn
;;   bz  1f
;;   dec Rn
;;   rpt Rn
;;   <shift> Rm
;;   inc Rn
;; 1:
;;
;; But is longer than calling a helper function, and we are mostly
;; concerned with code size.  FIXME: Maybe enable a sequence like
;; this at -O3 and above ?
;;
;; Note - we ignore shift counts of less than one or more than 15.
;; This is permitted by the ISO C99 standard as such shifts result
;; in "undefined" behavior.  [6.5.7 (3)]
;;
;; We avoid emitting insns in msp430_expand_shift, since we would have to handle
;; many extra cases such as op0 != op1, or, op0 or op1 in memory.  Instead we
;; let reload coerce op0 and op1 into the same register.

(define_expand "<shift_insn><mode>3"
  [(set (match_operand:HDI		  0 "msp430_general_dst_nonv_operand")
	(any_shift:HDI (match_operand:HDI 1 "general_operand")
		       (match_operand:HDI 2 "general_operand")))]
  ""
  {
    if (msp430_expand_shift (<CODE>, <MODE>mode, operands))
      DONE;
    /* Otherwise, fallthrough.  */
  }
)

;; All 430 HImode constant shifts
(define_insn "<shift_insn>hi3_430"
  [(set (match_operand:HI		0 "msp430_general_dst_nonv_operand" "=rm")
	(any_shift:HI (match_operand:HI 1 "general_operand"       "0")
		      (match_operand:HI 2 "const_int_operand"     "n")))]
  "!msp430x"
  "* msp430_output_asm_shift_insns (<CODE>, HImode, operands, false); return \"\";"
  [(set (attr "length")
	(symbol_ref "msp430_output_asm_shift_insns (<CODE>, HImode, operands, true)"))
   (set_attr "type" "single")]
)

;; All 430 and 430X SImode constant shifts
(define_insn "<shift_insn>si3_const"
  [(set (match_operand:SI		0 "msp430_general_dst_nonv_operand" "=rm")
	(any_shift:SI (match_operand:SI 1 "general_operand"       "0")
		      (match_operand:SI 2 "const_int_operand"     "n")))]
  ""
  "* msp430_output_asm_shift_insns (<CODE>, SImode, operands, false); return \"\";"
  [(set (attr "length")
	(symbol_ref "msp430_output_asm_shift_insns (<CODE>, SImode, operands, true)"))
   (set_attr "type" "single")]
)

(define_insn "ashl<mode>3_430x"
  [(set (match_operand:HPSI		 0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
	(ashift:HPSI (match_operand:HPSI 1 "general_operand" 		     "0 ,0,0,0")
		     (match_operand:HPSI 2 "const_int_operand" 		     "M ,P,K,i")))]
  "msp430x"
  "@
  RLAM%b0\t%2, %0
  RPT\t%2 { RLAX%b0\t%0
  RPT\t#16 { RLAX%b0\t%0 { RPT\t%W2 { RLAX%b0\t%0
  # undefined behavior left shift of %1 by %2"
  [(set_attr "length" "2,4,8,0")
   (set_attr "type" "single")]
)

(define_insn "ashr<mode>3_430x"
  [(set (match_operand:HPSI		   0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
	(ashiftrt:HPSI (match_operand:HPSI 1 "general_operand"	  	     "0,0,0,0")
		       (match_operand:HPSI 2 "const_int_operand" 	     "M,P,K,i")))]
  "msp430x"
  "@
  RRAM%b0\t%2, %0
  RPT\t%2 { RRAX%b0\t%0
  RPT\t#16 { RRAX%b0\t%0 { RPT\t%W2 { RRAX%b0\t%0
  # undefined behavior arithmetic right shift of %1 by %2"
  [(set_attr "length" "2,4,8,0")
   (set_attr "type" "single")]
)

(define_insn "lshr<mode>3_430x"
  [(set (match_operand:HPSI		   0 "msp430_general_dst_nonv_operand" "=r,r,r,r")
	(lshiftrt:HPSI (match_operand:HPSI 1 "general_operand"	  	     "0,0,0,0")
		       (match_operand:HPSI 2 "const_int_operand" 	     "M,P,K,i")))]
  "msp430x"
  "@
  RRUM%b0\t%2, %0
  RPT\t%2 { RRUX%b0\t%0
  RPT\t#16 { RRUX%b0\t%0 { RPT\t%W2 { RRUX%b0\t%0
  # undefined behavior logical right shift of %1 by %2"
  [(set_attr "length" "2,4,8,0")
   (set_attr "type" "single")]
)

;;------------------------------------------------------------
;; Function Entry/Exit

(define_expand "prologue"
  [(const_int 0)]
  ""
  "msp430_expand_prologue (); DONE;"
)

(define_expand "epilogue"
  [(const_int 0)]
  ""
  "msp430_expand_epilogue (0); DONE;"
)

(define_insn "epilogue_helper"
  [(set (pc)
	(unspec_volatile [(match_operand 0 "immediate_operand" "i")] UNS_EPILOGUE_HELPER))
   (return)]
  "!msp430x"
  "BR%Q0\t#__mspabi_func_epilog_%J0"
  [(set_attr "length" "2")]
)

(define_insn "prologue_start_marker"
  [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_START_MARKER)]
  ""
  "; start of prologue"
  [(set_attr "length" "0")]
)

(define_insn "prologue_end_marker"
  [(unspec_volatile [(const_int 0)] UNS_PROLOGUE_END_MARKER)]
  ""
  "; end of prologue"
  [(set_attr "length" "0")]
)

(define_insn "epilogue_start_marker"
  [(unspec_volatile [(const_int 0)] UNS_EPILOGUE_START_MARKER)]
  ""
  "; start of epilogue"
  [(set_attr "length" "0")]
)

;; This makes the linker add a call to exit() after the call to main()
;; in crt0
(define_insn "msp430_refsym_need_exit"
  [(unspec_volatile [(const_int 0)] UNS_REFSYM_NEED_EXIT)]
  ""
  ".refsym\t__crt0_call_exit"
  [(set_attr "length" "0")]
)

;;------------------------------------------------------------
;; Jumps

(define_expand "call"
  [(call:HI (match_operand 0 "")
	    (match_operand 1 ""))]
  ""
  ""
)

(define_insn "call_internal"
  [(call (mem:HI (match_operand 0 "general_operand" "rYci"))
	 (match_operand 1 ""))]
  ""
  "CALL%Q0\t%0"
  [(set_attr "extension" "none")
   (set_attr "type" "single")]
)

(define_expand "call_value"
  [(set (match_operand          0 "register_operand")
	(call:HI (match_operand 1 "general_operand")
		 (match_operand 2 "")))]
  ""
  ""
)

(define_insn "call_value_internal"
  [(set (match_operand               0 "register_operand" "=r")
	(call (mem:HI (match_operand 1 "general_operand" "rYci"))
	      (match_operand 2 "")))]
  ""
  "CALL%Q0\t%1"
  [(set_attr "extension" "none")
   (set_attr "type" "single")]
)

(define_insn "msp430_return"
  [(return)]
  ""
  { return msp430_is_interrupt_func () ? "RETI" : (TARGET_LARGE ? "RETA" : "RET"); }
  [(set_attr "length" "2")]
)

;; This pattern is NOT, as expected, a return pattern.  It's called
;; before reload and must only store its operands, and emit a
;; placeholder where the epilog needs to be.  AFTER reload, the
;; placeholder should get expanded into a regular-type epilogue that
;; also does the EH return.
(define_expand "eh_return"
  [(match_operand:HI 0 "")]
  ""
  "msp430_expand_eh_return (operands[0]);
   emit_jump_insn (gen_msp430_eh_epilogue ());
   emit_barrier ();
   DONE;"
)

;; This is the actual EH epilogue.  We emit it in the pattern above,
;; before reload, and convert it to a real epilogue after reload.
(define_insn_and_split "msp430_eh_epilogue"
  [(eh_return)]
  ""
  "#"
  "reload_completed"
  [(const_int 0)]
  "msp430_expand_epilogue (1); DONE;"
  [(set_attr "length" "40")]
)

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "BR%Q0\t#%l0"
  [(set_attr "length" "4")]
)

;; FIXME: GCC currently (8/feb/2013) cannot handle symbol_refs
;; in indirect jumps (cf gcc.c-torture/compile/991213-3.c).
(define_insn "indirect_jump"
  [(set (pc)
	(match_operand 0 "nonimmediate_operand" "rYl"))]
  ""
  "BR%Q0\t%0"
  [(set (attr "length")
	(if_then_else (match_operand 0 "register_operand" "")
		      (const_int 2)
		      (const_int 4)))]
)

;;------------------------------------------------------------
;; Various Conditionals

(define_expand "cbranch<mode>4"
  [(parallel [(set (pc) (if_then_else
			 (match_operator 0 ""
					 [(match_operand:QHI 1 "msp430_general_dst_nonv_operand")
					  (match_operand:QHI 2 "general_operand")])
			 (label_ref (match_operand 3 "" ""))
			 (pc)))
	      (clobber (reg:BI CARRY))]
  )]
  ""
  "msp430_fixup_compare_operands (<MODE>mode, operands);"
)

(define_insn "cbranchpsi4_real"
  [(set (pc) (if_then_else
	      (match_operator                     0 "msp430_cmp_operator"
			      [(match_operand:PSI 1 "msp430_general_dst_nonv_operand" "r,rYs,rm")
			       (match_operand:PSI 2 "general_operand"      "rLs,rYsi,rmi")])
	      (label_ref (match_operand		  3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
  CMP%Q0\t%2, %1 { J%0\t%l3
  CMPX.A\t%2, %1 { J%0\t%l3
  CMPX.A\t%2, %1 { J%0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "cbranchqi4_real"
  [(set (pc) (if_then_else
	      (match_operator                    0 "msp430_cmp_operator"
			      [(match_operand:QI 1 "msp430_general_dst_nonv_operand" "rYsYx,rm")
			       (match_operand:QI 2 "general_operand"      "rYsYxi,rmi")])
              (label_ref (match_operand          3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   CMP.B\t%2, %1 { J%0\t%l3
   CMPX.B\t%2, %1 { J%0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "cbranchhi4_real"
  [(set (pc) (if_then_else
	      (match_operator                    0 "msp430_cmp_operator"
			      [(match_operand:HI 1 "msp430_general_dst_nonv_operand" "rYsYx,rm")
			       (match_operand:HI 2 "general_operand"      "rYsYxi,rmi")])
              (label_ref (match_operand          3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   CMP.W\t%2, %1 { J%0\t%l3
   CMPX.W\t%2, %1 { J%0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "cbranchpsi4_reversed"
  [(set (pc) (if_then_else
	      (match_operator                     0 "msp430_reversible_cmp_operator"
			      [(match_operand:PSI 1 "general_operand" "rLs,rYsi,rmi")
			       (match_operand:PSI 2 "msp430_general_dst_nonv_operand" "r,rYs,rm")])
              (label_ref (match_operand           3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
  CMP%Q0\t%1, %2 { J%R0\t%l3
  CMPX.A\t%1, %2 { J%R0\t%l3
  CMPX.A\t%1, %2 { J%R0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "cbranchqi4_reversed"
  [(set (pc) (if_then_else
	      (match_operator                    0 "msp430_reversible_cmp_operator"
			      [(match_operand:QI 1 "general_operand" "rYsYxi,rmi")
			       (match_operand:QI 2 "msp430_general_dst_nonv_operand" "rYsYx,rm")])
              (label_ref (match_operand          3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   CMP.B\t%1, %2 { J%R0\t%l3
   CMPX.B\t%1, %2 { J%R0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "cbranchhi4_reversed"
  [(set (pc) (if_then_else
	      (match_operator                    0 "msp430_reversible_cmp_operator"
			      [(match_operand:HI 1 "general_operand" "rYsYxi,rmi")
			       (match_operand:HI 2 "msp430_general_dst_nonv_operand" "rYsYx,rm")])
              (label_ref (match_operand          3 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   CMP.W\t%1, %2 { J%R0\t%l3
   CMPX.W\t%1, %2 { J%R0\t%l3"
  [(set_attr "extra_length" "2")
   (set_attr "type" "cmp")]
)

(define_insn "*bitbranch<mode>4"
  [(set (pc) (if_then_else
	      (ne (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
			   (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
		  (const_int 0))
	      (label_ref (match_operand 2 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   BIT%x0%b0\t%1, %0 { JNE\t%l2
   BITX%b0\t%1, %0 { JNE\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4"
  [(set (pc) (if_then_else
	      (eq (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
			   (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
		  (const_int 0))
	      (label_ref (match_operand 2 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   BIT%x0%b0\t%1, %0 { JEQ\t%l2
   BITX%b0\t%1, %0 { JEQ\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4"
  [(set (pc) (if_then_else
	      (eq (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
			   (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
		  (const_int 0))
	      (pc)
	      (label_ref (match_operand 2 "" ""))))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
  BIT%x0%b0\t%1, %0 { JNE\t%l2
  BITX%b0\t%1, %0 { JNE\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4"
  [(set (pc) (if_then_else
	      (ne (and:QHI (match_operand:QHI 0 "msp430_general_dst_operand" "rYsYx,rm")
			   (match_operand:QHI 1 "msp430_general_operand" "rYsYxi,rmi"))
		  (const_int 0))
	      (pc)
	      (label_ref (match_operand 2 "" ""))))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
  BIT%x0%b0\t%1, %0 { JEQ\t%l2
  BITX%b0\t%1, %0 { JEQ\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

;;------------------------------------------------------------
;; zero-extract versions of the above

(define_insn "*bitbranch<mode>4_z"
  [(set (pc) (if_then_else
	      (ne (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rYs,rm")
				    (const_int 1)
				    (match_operand 1 "const_0_to_15_operand" "i,i"))
		  (const_int 0))
	      (label_ref (match_operand 2 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "@
   BIT%x0%b0\t%p1, %0 { JNE\t%l2
   BIT%X0%b0\t%p1, %0 { JNE\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4_z"
  [(set (pc) (if_then_else
	      (eq (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
				   (const_int 1)
				   (match_operand 1 "const_0_to_15_operand" "i"))
		  (const_int 0))
	      (label_ref (match_operand 2 "" ""))
	      (pc)))
   (clobber (reg:BI CARRY))
   ]
  ""
  "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4_z"
  [(set (pc) (if_then_else
	      (eq (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
				   (const_int 1)
				   (match_operand 1 "const_0_to_15_operand" "i"))
		  (const_int 0))
	      (pc)
	      (label_ref (match_operand 2 "" ""))))
   (clobber (reg:BI CARRY))
   ]
  ""
  "BIT%X0%b0\t%p1, %0 { JNE\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

(define_insn "*bitbranch<mode>4_z"
  [(set (pc) (if_then_else
	      (ne (zero_extract:HI (match_operand:QHI 0 "msp430_general_dst_operand" "rm")
				   (const_int 1)
				   (match_operand 1 "const_0_to_15_operand" "i"))
		  (const_int 0))
	      (pc)
	      (label_ref (match_operand 2 "" ""))))
   (clobber (reg:BI CARRY))
   ]
  ""
  "BIT%X0%b0\t%p1, %0 { JEQ\t%l2"
  [(set_attr "extra_length" "2")
   (set_attr "type" "double")]
)

;;------------------------------------------------------------
;; Misc

(define_insn "nop"
  [(const_int 0)]
  "1"
  "NOP"
  [(set_attr "length" "2")]
)

(define_insn "disable_interrupts"
  [(unspec_volatile [(const_int 0)] UNS_DINT)]
  ""
  "DINT \; NOP"
  [(set_attr "length" "2")]
)

(define_insn "enable_interrupts"
  [(unspec_volatile [(const_int 0)] UNS_EINT)]
  ""
  "EINT"
  [(set_attr "length" "2")]
)

(define_insn "push_intr_state"
  [(unspec_volatile [(const_int 0)] UNS_PUSH_INTR)]
  ""
  "PUSH\tSR"
  [(set_attr "length" "2")]
)

(define_insn "pop_intr_state"
  [(unspec_volatile [(const_int 0)] UNS_POP_INTR)]
  ""
  "POP\tSR"
  [(set_attr "length" "2")]
)

;; Clear bits in the copy of the status register that is currently
;; saved on the stack at the top of the interrupt handler.
(define_insn "bic_SR"
  [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIC_SR)]
  ""
  "BIC.W\t%0, %O0(SP)"
  [(set_attr "type" "single")
   (set_attr "extra_length" "2")]
)

;; Set bits in the copy of the status register that is currently
;; saved on the stack at the top of the interrupt handler.
(define_insn "bis_SR"
  [(unspec_volatile [(match_operand 0 "nonmemory_operand" "ir")] UNS_BIS_SR)]
  ""
  "BIS.W\t%0, %O0(SP)"
  [(set_attr "type" "single")
   (set_attr "extra_length" "2")]
)

;; For some reason GCC is generating (set (reg) (and (neg (reg)) (int)))
;; very late on in the compilation and not splitting it into separate
;; instructions, so we provide a pattern to support it here.
(define_insn "andneghi3"
  [(set (match_operand:HI		  0 "register_operand" "=r,r")
	(and:HI (neg:HI (match_operand:HI 1 "general_operand"  "0,rm"))
		(match_operand		  2 "immediate_operand" "n,n")))]
  ""
  "@
  INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0
  MOV%X1.W\t%1, %0 { INV.W\t%0 { INC.W\t%0 { AND.W\t%2, %0"
  [(set_attr "length" "12,14")
   (set_attr "type" "double")]
)


(define_insn "delay_cycles_start"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")]
		    UNS_DELAY_START)]
  ""
  "; Begin %J0 cycle delay"
  [(set_attr "length" "0")]
)

(define_insn "delay_cycles_end"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")]
		    UNS_DELAY_END)]
  ""
  "; End %J0 cycle delay"
  )

(define_insn "delay_cycles_32"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
		     (match_operand 1 "immediate_operand" "i")
		     ] UNS_DELAY_32)]
  ""
  "PUSH	r13
	PUSH	r14
	MOV.W	%A0, r13
	MOV.W	%B0, r14
1:	SUB.W	#1, r13
	SUBC.W	#0, r14
	JNE	1b
	TST.W	r13
	JNE	1b
	POP	r14
	POP	r13"
  [(set_attr "length" "32")]
)

(define_insn "delay_cycles_32x"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
		     (match_operand 1 "immediate_operand" "i")
		     ] UNS_DELAY_32X)]
  ""
  "PUSHM.A	#2,r14
	MOV.W	%A0, r13
	MOV.W	%B0, r14
1:	SUB.W	#1, r13
	SUBC.W	#0, r14
	JNE	1b
	TST.W	r13
	JNE	1b
	POPM.A	#2,r14"
  [(set_attr "length" "28")]
)

(define_insn "delay_cycles_16"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
		     (match_operand 1 "immediate_operand" "i")
		     ] UNS_DELAY_16)]
  ""
  "PUSH	r13
	MOV.W	%0, r13
1:	SUB.W	#1, r13
	JNE	1b
	POP	r13"
  [(set_attr "length" "14")]
)

(define_insn "delay_cycles_16x"
  [(unspec_volatile [(match_operand 0 "immediate_operand" "i")
		     (match_operand 1 "immediate_operand" "i")
		     ] UNS_DELAY_16X)]
  ""
  "PUSHM.A	#1,r13
	MOV.W	%0, r13
1:	SUB.W	#1, r13
	JNE	1b
	POPM.A	#1,r13"
  [(set_attr "length" "14")]
)

(define_insn "delay_cycles_2"
  [(unspec_volatile [(const_int 0) ] UNS_DELAY_2)]
  ""
  "JMP	.+2"
  [(set_attr "length" "2")]
)

(define_insn "delay_cycles_1"
  [(unspec_volatile [(const_int 0) ] UNS_DELAY_1)]
  ""
  "NOP"
  [(set_attr "length" "2")]
)

(define_expand "mulhi3"
  [(set (match_operand:HI			   0 "register_operand" "=r")
	(mult:HI (match_operand:HI 1 "register_operand" "%0")
		 (match_operand:HI 2 "register_operand" "r")))]
  "msp430_has_hwmult ()"
  {
    msp430_expand_helper (operands, "__mspabi_mpyi", false);
    DONE;
  }
)

(define_expand "mulsi3"
  [(set (match_operand:SI			   0 "register_operand" "=r")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "r")))]
  "msp430_has_hwmult ()"
  {
    msp430_expand_helper (operands, "__mspabi_mpyl", false);
    DONE;
  }
)

; libgcc helper functions for widening multiplication aren't currently
; generated by gcc, so we can't catch them later and map them to the mspabi
; functions.
; We catch the patterns here and either generate a call to the helper function,
; or emit the hardware multiply instruction sequence inline.
;
; If we don't have hardware multiply support, it will generally be slower and
; result in larger code to call the mspabi library function to perform the
; widening multiplication than just leaving GCC to widen the arguments itself.

(define_expand "mulhisi3"
  [(set (match_operand:SI			   0 "register_operand" "=r")
	(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
		 (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "msp430_has_hwmult ()"
  {
    /* Leave the other case for the inline insn.  */
    if (!(optimize > 2 && msp430_has_hwmult ()))
    {
      msp430_expand_helper (operands, "__mspabi_mpysl", false);
      DONE;
    }
  }
)

(define_expand "umulhisi3"
  [(set (match_operand:SI			   0 "register_operand" "=r")
	(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0"))
		 (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "msp430_has_hwmult ()"
  {
    /* Leave the other case for the inline insn.  */
    if (!(optimize > 2 && msp430_has_hwmult ()))
    {
      msp430_expand_helper (operands, "__mspabi_mpyul", false);
      DONE;
    }
  }
)

(define_expand "mulsidi3"
  [(set (match_operand:DI			   0 "register_operand" "=r")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
  "msp430_has_hwmult ()"
  {
    /* Leave the other case for the inline insn.  */
    if (!(optimize > 2 && msp430_has_hwmult ()))
    {
      msp430_expand_helper (operands, "__mspabi_mpysll", false);
      DONE;
    }
  }
)

(define_expand "umulsidi3"
  [(set (match_operand:DI			   0 "register_operand" "=r")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
  "msp430_has_hwmult ()"
  {
    /* Leave the other case for the inline insn.  */
    if (!(optimize > 2 && msp430_has_hwmult ()))
    {
      msp430_expand_helper (operands, "__mspabi_mpyull", false);
      DONE;
    }
  }
)


(define_insn "*mulhisi3_inline"
  [(set (match_operand:SI                          0 "register_operand" "=r")
	(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
		 (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "optimize > 2 && msp430_has_hwmult ()"
  "*
    if (msp430_use_f5_series_hwmult ())
      return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x04C2 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0 { POP.W sr\";
    else
      return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0132 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\";
  "
  [(set_attr "length" "24")]
)

(define_insn "*umulhisi3_inline"
  [(set (match_operand:SI                          0 "register_operand" "=r")
	(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0"))
		 (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "optimize > 2 && msp430_has_hwmult ()"
  "*
    if (msp430_use_f5_series_hwmult ())
      return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x04C0 { MOV.W %2, &0x04C8 { MOV.W &0x04CA, %L0 { MOV.W &0x04CC, %H0 { POP.W sr\";
    else
      return \"PUSH.W sr { DINT { NOP { MOV.W %1, &0x0130 { MOV.W %2, &0x0138 { MOV.W &0x013A, %L0 { MOV.W &0x013C, %H0 { POP.W sr\";
  "
  [(set_attr "length" "24")]
)

(define_insn "*mulsidi3_inline"
  [(set (match_operand:DI                          0 "register_operand" "=r")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
  "optimize > 2 && msp430_has_hwmult ()"
  "*
    if (msp430_use_f5_series_hwmult ())
      return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x04D4 { MOV.W %H1, &0x04D6 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0 { POP.W sr\";
    else
      return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0144 { MOV.W %H1, &0x0146 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\";
  "
  [(set_attr "length" "40")]
)

(define_insn "*umulsidi3_inline"
  [(set (match_operand:DI                          0 "register_operand" "=r")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
  "optimize > 2 && msp430_has_hwmult ()"
  "*
    if (msp430_use_f5_series_hwmult ())
      return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x04D0 { MOV.W %H1, &0x04D2 { MOV.W %L2, &0x04E0 { MOV.W %H2, &0x04E2 { MOV.W &0x04E4, %A0 { MOV.W &0x04E6, %B0 { MOV.W &0x04E8, %C0 { MOV.W &0x04EA, %D0 { POP.W sr\";
    else
      return \"PUSH.W sr { DINT { NOP { MOV.W %L1, &0x0140 { MOV.W %H1, &0x0142 { MOV.W %L2, &0x0150 { MOV.W %H2, &0x0152 { MOV.W &0x0154, %A0 { MOV.W &0x0156, %B0 { MOV.W &0x0158, %C0 { MOV.W &0x015A, %D0 { POP.W sr\";
  "
  [(set_attr "length" "40")]
)
